package com.itsu.admin.service.impl;

import cn.hutool.json.JSONUtil;
import com.itsu.admin.model.JobResp;
import com.itsu.admin.model.PageReq;
import com.itsu.admin.model.ListResult;
import com.itsu.admin.mvc.Result;
import com.itsu.admin.service.TaskService;
import com.itsu.job.components.Const;
import com.itsu.job.config.JobProperties;
import com.itsu.job.rpc.util.RpcCallUtil;
import com.itsu.job.rpc.util.RpcResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Jerry.su
 * @Date 2022/1/2 16:56
 */
@Slf4j
@Service
public class TaskServiceImpl implements TaskService {

    @Resource
    private JobProperties jobProperties;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public ListResult list(PageReq pageReq) {
        ListResult listResult = new ListResult();
        List<JobResp> jobList = new ArrayList<>();
        String nodeStr = stringRedisTemplate.opsForValue().get(Const.REGISTER_RPC_NODES);
        if (!StringUtils.hasText(nodeStr)) {
            log.debug("there is no server node register now");
            listResult.setCode(0);
            listResult.setCount(0);
        } else {
            String[] nodes = nodeStr.split(Const.RPC_NODE_SPLIT);
            for (String node : nodes) {
                String status = stringRedisTemplate.opsForValue().get(node);
                if (Const.UP_STATUS.equals(status)) {
                    RpcResult result = RpcCallUtil.call(node + "/list", "", jobProperties.getRpc().getAccessToken());
                    if (result.getCode() == 0) {
                        List<JobResp> list = (List<JobResp>) result.getData();
                        jobList.addAll(list);
                    } else {
                        log.debug("current node {} found none configured job", node);
                    }
                } else {
                    log.debug("current node {} is down status, skip to get job from it", node);
                }
            }

            int page = pageReq.getPage();
            int limit = pageReq.getLimit();
            int startIndex = (page - 1) * limit;

//        if (startIndex < jobList.size()) {
            List<JobResp> list = new ArrayList<>();
            int num = 0;
            if (startIndex + limit <= jobList.size()) {
                num = startIndex + limit;
            } else {
                num = jobList.size();
            }
            for (int i = startIndex; i < num; i++) {
                list.add(jobList.get(i));
            }
            listResult.setData(list);
//        }
            listResult.setCode(0);
            listResult.setCount(jobList.size());
        }
        return listResult;
    }

    @Override
    public Result changeStatus(String jobId, int status, String registerHost) {
        String rpcRegisterAddress = getRpcRegisterAddress(registerHost);
        String requestData = JSONUtil.toJsonStr(JSONUtil.createObj().putOpt("jobId", jobId));
        RpcResult res = RpcCallUtil.call(rpcRegisterAddress + "/status", requestData, jobProperties.getRpc().getAccessToken());
        if (res.getCode() != 0) {
            return Result.error("can not get this job current status");
        }
        int jobStat = (int) res.getData();

        if (status == 1) {
            if (jobStat == 1) {
                return Result.error("already started");
            } else {
                String req = JSONUtil.toJsonStr(JSONUtil.createObj().putOpt("jobId", jobId));
                res = RpcCallUtil.call(rpcRegisterAddress + "/schedule", req, jobProperties.getRpc().getAccessToken());
                if (res.getCode() == 0) {
                    return Result.ok();
                } else {
                    return Result.error(res.getMsg());
                }
            }
        } else {
            if (jobStat == 1) {
                String req = JSONUtil.toJsonStr(JSONUtil.createObj().putOpt("jobId", jobId).putOpt("type", 1));
                res = RpcCallUtil.call(rpcRegisterAddress + "/stop", req, jobProperties.getRpc().getAccessToken());
                if (res.getCode() == 0) {
                    return Result.ok();
                } else {
                    return Result.error(res.getMsg());
                }
            } else {
                return Result.error("not started");
            }
        }

    }

    @Override
    public Result runJob(String jobId, String registerHost) {
        String rpcRegisterAddress = getRpcRegisterAddress(registerHost);
        String requestData = JSONUtil.toJsonStr(JSONUtil.createObj().putOpt("jobId", jobId));
        RpcResult result = RpcCallUtil.call(rpcRegisterAddress + "/run", requestData, jobProperties.getRpc().getAccessToken());
        if (result.getCode() == 0) {
            return Result.ok();
        } else {
            return Result.error(-1);
        }
    }

    public String getRpcRegisterAddress(String registerHost) {
        String nodeStr = stringRedisTemplate.opsForValue().get(Const.REGISTER_RPC_NODES);
        nodeStr = nodeStr == null ? "" : nodeStr;
        for (String node : nodeStr.split(Const.RPC_NODE_SPLIT)) {
            if (node.contains(registerHost)) {
                return node;
            }
        }
        throw new RuntimeException("can not find the rpc register address of this node " + registerHost);
    }

    @Override
    public ListResult search(String key) {
        ListResult listResult = new ListResult();
        String nodeStr = stringRedisTemplate.opsForValue().get(Const.REGISTER_RPC_NODES);
        if (!StringUtils.hasText(nodeStr)) {
            log.debug("there is no server node register now");
            listResult.setCode(0);
            listResult.setCount(0);
        } else {
            List<JobResp> jobList = new ArrayList<>();
            for (String node : nodeStr.split(Const.RPC_NODE_SPLIT)) {
                String requestData = JSONUtil.toJsonStr(JSONUtil.createObj().putOpt("key", key));
                RpcResult result = RpcCallUtil.call(node + "/search", requestData, jobProperties.getRpc().getAccessToken());
                if (result.getCode() == 0) {
                    List<JobResp> list = (List<JobResp>) result.getData();
                    jobList.addAll(list);
                } else {
                    log.debug("current node {} found none configured job contains {} ", node, key);
                }
            }
            listResult.setCount(jobList.size());
            listResult.setCode(0);
            listResult.setData(jobList);
        }
        return listResult;
    }
}
